package file

import (
	"errors"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
)

var workDir string

func init() {
	dir, err := os.Getwd()
	if err != nil {
		return
	}
	workDir = strings.ReplaceAll(dir, "\\", "/")
}

func Existed(filePath string) bool {
	fileInfo, _ := os.Stat(filePath)
	return fileInfo != nil
}

func IsDir(filePath string) bool {
	fileInfo, _ := os.Stat(filePath)
	return fileInfo != nil && fileInfo.IsDir()
}

func Parent(filePath string) string {
	return GetAbsPath(Join(filePath, "../"))
}

func Clean(filePath string) string {
	return strings.ReplaceAll(filepath.Clean(filePath), "\\", "/")
}

func Join(s ...string) string {
	return strings.ReplaceAll(filepath.Join(s...), "\\", "/")
}

func GetRelativePath(base string, test string) string {
	baseAbs := GetAbsPath(base)
	testAbs := GetAbsPath(test)
	shortLen := len(baseAbs)
	if len(testAbs) < shortLen {
		shortLen = len(testAbs)
	}
	sharedPath := ""
	for index := 0; index < shortLen; index++ {
		c1 := baseAbs[index]
		c2 := testAbs[index]
		if c1 == c2 {
			sharedPath += string(c1)
		} else {
			break
		}
	}
	sharedLen := len(sharedPath)
	sharedPath = Clean(sharedPath)
	baseToShared := ""
	tmpPath := baseAbs
	lastPath := tmpPath
	for {
		if tmpPath == sharedPath {
			break
		}
		tmpPath = Join(tmpPath, "../")
		if lastPath == tmpPath {
			break
		}
		lastPath = tmpPath
		baseToShared += "../"
	}
	sharedToTest := Clean(testAbs[sharedLen:])
	return Join(baseToShared, sharedToTest)
}

func Split(filePath string) (dir, file string) {
	abs := GetAbsPath(filePath)
	return path.Split(abs)
}

func WriteFile(filePath string, data []byte, perm os.FileMode) error {
	err := os.MkdirAll(Parent(filePath), perm)
	if err != nil {
		return err
	}
	return os.WriteFile(filePath, data, perm)
}

func GetAbsPath(filePath string) string {
	realAbs := filepath.IsAbs(filePath)
	if runtime.GOOS == "windows" {
		ss := strings.Split(filePath, ":\\")
		if len(ss) == 2 {
			realAbs = true
		}
	}
	if !realAbs {
		return Clean(Join(workDir, filePath))
	}
	return Clean(filePath)
}

func RangeAllFile(absPath string, accept func(absPath string) bool, walkFile func(absPath string)) error {
	absPath = GetAbsPath(absPath)
	if !Existed(absPath) {
		return errors.New("file not existed")
	}
	if IsDir(absPath) {
		dirEntries, err := os.ReadDir(absPath)
		if err != nil {
			return err
		}
		for _, dirEntry := range dirEntries {
			entryInfo, err := dirEntry.Info()
			if err != nil {
				return err
			}
			fullPath := Join(absPath, entryInfo.Name())
			err = RangeAllFile(fullPath, accept, walkFile)
			if err != nil {
				return err
			}
		}
	} else {
		if accept(absPath) {
			walkFile(absPath)
		}
	}
	return nil
}

func RangeAllFileAndDir(absPath string, dirAccept func(absPath string) bool, dirWalk func(absPath string), fileAccept func(absPath string) bool, walkFile func(absPath string)) error {
	absPath = GetAbsPath(absPath)
	if !Existed(absPath) {
		return errors.New("file not existed")
	}
	if IsDir(absPath) {
		if !dirAccept(absPath) {
			return nil
		}
		dirWalk(absPath)
		dirEntries, err := os.ReadDir(absPath)
		if err != nil {
			return err
		}
		for _, dirEntry := range dirEntries {
			entryInfo, err := dirEntry.Info()
			if err != nil {
				return err
			}
			fullPath := Join(absPath, entryInfo.Name())
			err = RangeAllFile(fullPath, fileAccept, walkFile)
			if err != nil {
				return err
			}
		}
	} else {
		if fileAccept(absPath) {
			walkFile(absPath)
		}
	}
	return nil
}

func WorkDir() string {
	return workDir
}

func CalculateGoPkgFullNameWithPath(pkgPath string, projectRoot string, projectName string) string {
	relativeToRootPath := GetRelativePath(projectRoot, pkgPath)
	return CalculateGoPkgFullNameWithRelativeToRoot(relativeToRootPath, projectName)
}

func CalculateGoPkgFullNameWithRelativeToRoot(relativeToRootPath string, projectName string) string {
	return projectName + Clean(relativeToRootPath)
}

func CalculateRefNameAndImportName(targetPkg string, beImported string, projectName string) (refName, importName string) {
	if targetPkg == beImported {
		return "", ""
	}
	importName = strings.ReplaceAll(strings.ReplaceAll(strings.TrimPrefix(beImported, projectName+"/"), "/", "_"), ".", "_")
	refName = importName + "."
	return refName, importName
}
